import math

from matplotlib import pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

import utils.road as roadUtil
import utils.build as buildUtil
import utils.tree as treeUtil
import utils.water as waterUtil
import utils.mountain as mountainUtil
from scipy.spatial import KDTree
import numpy as np
import yaml
import sys

#此处配置无效，请使用config文件进行配置
max_iteration = 20000
max_long = 280
sys.setrecursionlimit(4000)
draw = 0
mode = 1
skipRoad = 0
paths = []
entrance_list = []
exit_list = []
find_step = [(7, 15, 20, 25, 30, 35),  # 1
             (3, 5, 10, 15, 20, 30),  # 2
             (4, 12, 18, 24, 28, 35),  # 3
             (4, 12, 18, 24, 28, 35),  # 4
             (4, 12, 18, 24, 28, 35),  # 5
             (4, 12, 18, 24, 28, 35),  # 6
             (3, 12, 18, 24, 28, 35),  # 7
             (7, 15, 20, 25, 30, 35),  # 8
             (4, 12, 18, 24, 28, 35),  # 9
             (4, 12, 18, 24, 28, 35),   # 10
             (1, 3, 5, 7, 9, 13)]  # 11
mapArea = 0
mapID = 1
nowQuestion = 1
debug = 1

scoreIndex_OC = 0
scoreIndex_H = 0
scoreIndex_MWrate = 0
scoreIndex_TreeVar = 0
scoreIndex_MountainS_I = 0
scoreIndex_b = 0

otherMap = ''

stepScores = []
scoreIndex_WaterS_I = 0


def init_data():
    global mapID
    global draw
    global max_iteration
    global max_long
    global entrance_list
    global exit_list
    global nowQuestion
    global debug
    global mapArea
    global skipRoad
    global scoreIndex_OC
    global scoreIndex_H
    global scoreIndex_MWrate
    global scoreIndex_TreeVar
    global scoreIndex_MountainS_I
    global scoreIndex_WaterS_I
    global otherMap
    try:
        with open('..\\config\\config.yaml', 'r', encoding='utf-8') as file:  # 建议指定编码，如 utf-8
            data = yaml.safe_load(file)  # 使用 safe_load 安全加载

            for k in range(1, 12):
                entrance_list.append((data['entrance'][f'{k}']['x'], data['entrance'][f'{k}']['y']))
                exit_list.append((data['exit'][f'{k}']['x'], data['exit'][f'{k}']['y']))

            mapID = (data['config']['mapID'])
            max_iteration = (data['config']['Q1']['max_iteration'])
            max_long = (data['config']['Q1']['max_long'])
            draw = (data['config']['Q1']['draw'])
            nowQuestion = (data['config']['nowQuestion'])
            debug = (data['config']['debug'])

            skipRoad = (data['config']['Q2']['skipRoad'])
            scoreIndex_OC = (data['config']['Q2']['scoreIndex']['OC'])
            scoreIndex_H = (data['config']['Q2']['scoreIndex']['H'])
            scoreIndex_MWrate = (data['config']['Q2']['scoreIndex']['MWrate'])
            scoreIndex_TreeVar = (data['config']['Q2']['scoreIndex']['TreeVar'])
            scoreIndex_MountainS_I = (data['config']['Q2']['scoreIndex']['MountainS_I'])
            scoreIndex_WaterS_I = (data['config']['Q2']['scoreIndex']['WaterS_I'])

            otherMap = (data['config']['Q3']['otherMap'])

            mapArea = (data['area'][f"{mapID}"])
    except FileNotFoundError:
        print("错误：未找到 YAML 文件。")
    except yaml.YAMLError as e:
        print(f"解析 YAML 文件时出错：{e}")


def onclick(event):
    if not debug: return
    # event.xdata 和 event.ydata 包含了鼠标点击处的数据坐标
    if event.xdata is not None and event.ydata is not None:  # 确保点击在坐标轴区域内
        print(f'Clicked at: (x={event.xdata:.6f}, y={event.ydata:.6f})')


def drawMap(road_points, corner_points, buildCentre):
    plt.rcParams['font.family'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
    plt.figure(figsize=(10, 10)).canvas.mpl_connect('button_press_event', onclick)
    # 绘制道路中心点序列：连线表示路径
    for road in road_points:
        x_road = [point[0] for point in road]
        y_road = [point[1] for point in road]
        plt.scatter(x_road, y_road, s=10, c='blue', alpha=0.5)  # 可选：显示点位置

    x_corner = [point[0] for point in corner_points]
    y_corner = [point[1] for point in corner_points]
    plt.scatter(x_corner, y_corner, s=30, c='red', alpha=0.5)  # 可选：显示点位置

    x_build = [point[0] for point in buildCentre]
    y_build = [point[1] for point in buildCentre]
    plt.scatter(x_build, y_build, s=30, c='purple', alpha=0.5)  # 可选：显示点位置

    plt.scatter(entrance_list[mapID - 1][0], entrance_list[mapID - 1][1], s=50, c='green', alpha=0.5)
    plt.scatter(exit_list[mapID - 1][0], exit_list[mapID - 1][1], s=50, c='green', alpha=0.5)
    plt.text(entrance_list[mapID - 1][0] + 100, entrance_list[mapID - 1][1] + 100, 'IN', fontsize=12, color='black',
             fontweight='bold')
    plt.text(exit_list[mapID - 1][0] - 100, exit_list[mapID - 1][1] - 100, 'OUT', fontsize=12, color='black',
             fontweight='bold')

    #for i in range(0, len(paths)):
    i = np.argmax(allScores)  # 只展示最高分
    for j in range(0, len(paths[i])):
        if j == 0:
            aPoint = [entrance_list[mapID - 1][0], entrance_list[mapID - 1][1]]
            bPoint = [[altPoints[paths[i][j]]][0][0], [altPoints[paths[i][j]]][0][1]]
        else:
            aPoint = [[altPoints[paths[i][j - 1]]][0][0], [altPoints[paths[i][j - 1]]][0][1]]
            bPoint = [[altPoints[paths[i][j]]][0][0], [altPoints[paths[i][j]]][0][1]]
        plt.plot([aPoint[0], bPoint[0]], [aPoint[1], bPoint[1]], c='red', linestyle='--')

    # plt.pause(0.001)
    # 添加图例和标签
    plt.title('ROAD')
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.grid(True, alpha=0.3)
    plt.axis('equal')  # 保证比例相等，避免失真

    # 添加比例尺和指北针（简单示例）
    plt.text(0.05, 0.95, '↑N', transform=plt.gca().transAxes, fontsize=12, weight='bold')  # 指北针
    plt.plot([0.1, 0.1], [0.1, 0.2], 'k-', linewidth=2)  # 比例尺线段

    plt.show()


def distance(point1, point2):
    """计算二维平面上两点之间的欧几里得距离"""
    x1, y1 = point1
    x2, y2 = point2
    return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)


# 判断某个线是否不按路径规划走
def isThroughWall(pointA, pointB):
    flag = 0
    n = math.ceil(distance(pointA, pointB) / 500)  # 0.5米一个点
    mediate_x = np.linspace(pointA[0], pointB[0], n + 2)
    mediate_y = np.linspace(pointA[1], pointB[1], n + 2)
    mediatePoints = np.column_stack((mediate_x, mediate_y))

    for point in mediatePoints:
        dist, idx = wall_kdtree.query(point, k=1)
        if mapID < 10:
            if dist > 3000:
                flag = flag + 1
        else:
            if dist > 300:
                flag = flag + 1
    if flag > (n / 4):
        return True
    else:
        return False


count = 0


# 以当前点为起点，搜寻可行的下一个点
def searchPath(nowPoint, afterPath):
    if not afterPath:
        afterPath.append(len(altPoints) - 2)
    global count
    count = count + 1
    search_percent = (count / max_iteration) * 100 + 1
    if search_percent > 100: search_percent = 100
    print(
        f'\r【系统提示】 正在进行路线规划: [{"█" * math.floor(60 * search_percent / 100)}{" " * math.floor(60 * (100 - search_percent) / 100)}] {search_percent:.0f}%',
        end='', flush=True)
    if count > max_iteration:
        return

    canPoint = []  # 经验证可行的点位
    closePoint = []  # 近距离的点位
    # 先从20米内找可行的路径点
    for i in range(0, len(altPoints)):
        point = altPoints[i]
        # 先从10~15米内找可行的路径点
        if nowPoint == i: continue
        if find_step[mapID - 1][1] * 1000 > distance(altPoints[nowPoint], point) > find_step[mapID - 1][0] * 1000:
            closePoint.append(i)
    # 验证可行性
    for i in closePoint:
        if i in afterPath:
            # 走回头路了
            continue
        if isThroughWall(altPoints[nowPoint], altPoints[i]):
            # 撞墙了
            continue
        canPoint.append(i)

    if not canPoint:
        closePoint.clear()
        for i in range(0, len(altPoints)):
            point = altPoints[i]
            # 没有合适的，从40米内找可行的路径点
            if nowPoint == i: continue
            if find_step[mapID - 1][2] > distance(altPoints[nowPoint], point) > find_step[mapID - 1][1] * 1000:
                closePoint.append(i)
        # 验证可行性
        for i in closePoint:
            if i in afterPath:
                # 走回头路了
                continue
            if isThroughWall(altPoints[nowPoint], altPoints[i]):
                # 撞墙了
                continue
            canPoint.append(i)
    if not canPoint:
        closePoint.clear()
        for i in range(0, len(altPoints)):
            point = altPoints[i]
            # 没有合适的，从60米内找可行的路径点
            if nowPoint == i: continue
            if find_step[mapID - 1][3] * 1000 > distance(altPoints[nowPoint], point) > find_step[mapID - 1][2] * 1000:
                closePoint.append(i)
        # 验证可行性
        for i in closePoint:
            if i in afterPath:
                # 走回头路了
                continue
            if isThroughWall(altPoints[nowPoint], altPoints[i]):
                # 撞墙了
                continue
            canPoint.append(i)
    if not canPoint:
        closePoint.clear()
        for i in range(0, len(altPoints)):
            point = altPoints[i]
            # 没有合适的，从80米内找可行的路径点
            if nowPoint == i: continue
            if find_step[mapID - 1][4] * 1000 > distance(altPoints[nowPoint], point) > find_step[mapID - 1][3] * 1000:
                closePoint.append(i)
        # 验证可行性
        for i in closePoint:
            if i in afterPath:
                # 走回头路了
                continue
            if isThroughWall(altPoints[nowPoint], altPoints[i]):
                # 撞墙了
                continue
            canPoint.append(i)
    if not canPoint:
        closePoint.clear()
        for i in range(0, len(altPoints)):
            point = altPoints[i]
            # 没有合适的，从100米内找可行的路径点
            if nowPoint == i: continue
            if find_step[mapID - 1][5] * 1000 > distance(altPoints[nowPoint], point) > find_step[mapID - 1][4] * 1000:
                closePoint.append(i)
        # 验证可行性
        for i in closePoint:
            if i in afterPath:
                # 走回头路了
                continue
            if isThroughWall(altPoints[nowPoint], altPoints[i]):
                # 撞墙了
                continue
            canPoint.append(i)
    # 以可行的路径点为起点，继续搜寻可行的路径点
    if canPoint:
        for next_i in canPoint:
            # 检查是否到达终点
            if next_i == (len(altPoints) - 1):
                # 到达终点,加入主记录
                afterPath.append(next_i)
                paths.append(afterPath)
                if debug: print("迭代次数：", count)
                if debug: print("    找到一条路径,长度为", len(afterPath))
            elif len(afterPath) > max_long:
                # 迭代过多，放弃
                if debug: print("迭代次数：", count)
                if debug: print("    迭代次数过多，放弃")
                continue
            else:
                # 未到达终点，继续搜寻
                afterPath.append(next_i)
                searchPath(next_i, afterPath.copy())
    # 如果在这里return，说明没有找到通往终点的路径（可能是由于距离过远，或者是走到了死胡同）
    return


def findFeasiblePath():

    paths.clear()
    searchPath(len(altPoints) - 2, paths.copy())

    return paths


# 将相近的两个点合并为一个点
def mergePoint(points, min_distance):
    for pointA in points:
        for pointB in points:
            if pointA == pointB: continue
            if distance(pointA, pointB) < min_distance:
                points.remove(pointB)
    return points


def generate_rays(A, B, dis, num_rays=21, total_spread_deg=20.0):
    """
    以点A为中心，向点B方向生成在指定扇形区域内均匀分布的射线。

    参数:
        A: 元组，中心点坐标 (x, y)。
        B: 元组，目标点坐标 (x, y)。
        num_rays: 射线数量（应为奇数，默认为5）。
        total_spread_deg: 总扇形区域角度（度），默认为30度（±15度）。
        ray_length: 射线长度（用于计算终点坐标），默认为100单位。

    返回:
        rays: 一个列表，包含每条射线的起点和终点坐标 [起点, 终点]。
              起点都是A，终点是计算出的射线端点。
    """

    # 计算向量AB
    dx = B[0] - A[0]
    dy = B[1] - A[1]
    # 计算向量长度并单位化
    length_unit = math.sqrt(dx ** 2 + dy ** 2)
    if length_unit == 0:
        raise ValueError("点A和点B不能重合")
    ux = dx / length_unit
    uy = dy / length_unit

    # 计算偏移角度（度）
    spread_start_deg = total_spread_deg / 2.0  # -15度
    # 生成均匀分布的偏移角度（包括中心）
    angles_deg = [spread_start_deg - (i * total_spread_deg / (num_rays - 1)) for i in range(num_rays)]
    # 示例：5条射线时，angles_deg = [-15.0, -7.5, 0.0, 7.5, 15.0]

    rays = []  # 存储射线起点和终点
    for angle_deg in angles_deg:
        # 将角度转换为弧度
        angle_rad = math.radians(angle_deg)
        # 应用旋转矩阵计算新方向向量
        new_ux = ux * math.cos(angle_rad) - uy * math.sin(angle_rad)
        new_uy = ux * math.sin(angle_rad) + uy * math.cos(angle_rad)
        # 计算射线终点（从A点出发，沿新方向向量延伸r）
        end_x = A[0] + new_ux * dis
        end_y = A[1] + new_uy * dis
        # 记录射线（起点为A，终点为计算出的点）
        rays.append((A, (end_x, end_y)))

    return rays


doneScore = {}


# 给一个线段打分
def scorePoint(nowPoint, nextPoint, tree_radius=0.25 * 1000, water_radius=0.25 * 1000, build_radius=0.25 * 1000,
               mount_radius=0.25 * 1000):
    pointScore = [0, 0, 0, 0]  # [树木得分，水体得分，建筑得分, 假山得分]
    if (nowPoint, nextPoint) in doneScore:
        return doneScore[(nowPoint, nextPoint)]
    dis = distance(nowPoint, nextPoint)
    rays = generate_rays(nowPoint, nextPoint, dis)
    for ray in rays:
        if pointScore == [1, 1, 1, 1]:
            break
        # 插值进行计算
        aimPoint = ray[1]
        # 将输入点转换为NumPy数组，便于计算
        p1 = np.array(nowPoint)
        p2 = np.array(aimPoint)
        # 分别在x轴和y轴方向上生成n+2个等间距的点（包括起点和终点）
        # 例如，n=3, 则生成 [0, 0.25, 0.5, 0.75, 1.0] * 向量 + 起点
        n = math.ceil(dis / (0.5 * 1000))  # 每0.1米检测一次碰撞，次数向上取整
        mediate_x = np.linspace(p1[0], p2[0], n + 2)
        mediate_y = np.linspace(p1[1], p2[1], n + 2)
        mediatePoints = np.column_stack((mediate_x, mediate_y))
        for mediatePoint in mediatePoints:
            # 检查树木
            if pointScore[0] == 0:
                dist, idx = tree_kdtree.query(mediatePoint, k=1)
                if dist < tree_radius: pointScore[0] = 1
            # 检查水体
            if pointScore[1] == 0:
                dist, idx = water_kdtree.query(mediatePoint, k=1)
                if dist < water_radius: pointScore[1] = 1
            # 检查建筑
            if pointScore[2] == 0:
                dist, idx = build_kdtree.query(mediatePoint, k=1)
                if dist < build_radius: pointScore[2] = 1
            # 检查假山
            if pointScore[3] == 0:
                dist, idx = mount_kdtree.query(mediatePoint, k=1)
                if dist < mount_radius: pointScore[3] = 1
    doneScore[(nowPoint, nextPoint)] = pointScore
    return pointScore


# 给一条指定的路线打分
def scorePath(path):
    global stepScores
    pathScore = [0, 0, 0, 0]  # [树木得分，水体得分，建筑得分, 假山得分]
    lastPathScore = [0, 0, 0, 0]
    stepScore = []
    for i in range(0, len(path) - 1):
        if altPoints[path[i]] == altPoints[path[i + 1]]: continue
        newScore = scorePoint(altPoints[path[i]], altPoints[path[i + 1]])
        # 对newScore进行处理
        pathScore[0] = pathScore[0] + ((newScore[0] - lastPathScore[0]) if newScore[0] > lastPathScore[0] else 0)
        pathScore[1] = pathScore[1] + ((newScore[1] - lastPathScore[1]) if newScore[1] > lastPathScore[1] else 0)
        pathScore[2] = pathScore[2] + ((newScore[2] - lastPathScore[2]) if newScore[2] > lastPathScore[2] else 0)
        pathScore[3] = pathScore[3] + ((newScore[3] - lastPathScore[3]) if newScore[3] > lastPathScore[3] else 0)
        #  记录stepScore
        stepScore.append([((newScore[0] - lastPathScore[0]) if newScore[0] > lastPathScore[0] else 0) +
                          ((newScore[1] - lastPathScore[1]) if newScore[1] > lastPathScore[1] else 0) +
                          ((newScore[2] - lastPathScore[2]) if newScore[2] > lastPathScore[2] else 0) +
                          ((newScore[3] - lastPathScore[3]) if newScore[3] > lastPathScore[3] else 0)
                          ])
        # 更新lastPathScore
        lastPathScore = newScore
        pathScore = list(map(lambda x, y: x + y, pathScore, newScore))
    stepScores.append(stepScore)
    return pathScore


# 计算Shannon多样性指数
def getShannon_H(proportions):
    H = 0
    for p in proportions:
        if p > 0:  # 避免ln(0)的错误
            H -= p * math.log(p)
    return H


# 计算单个点的开合度，数值越大则认为视野越开阔
def getPointOC_index(nowPoint, radius=40 * 1000, ray_n=360):
    # 生成均匀分布的角度值（0 到 2π）
    angles = np.linspace(0, 2 * np.pi, ray_n, endpoint=False)
    # 向量化计算所有点的 x 和 y 坐标
    x_c = nowPoint[0] + radius * np.cos(angles)
    y_c = nowPoint[1] + radius * np.sin(angles)
    # 将 x 和 y 坐标堆叠成一个数组
    ray_points = np.column_stack((x_c, y_c))

    totalDistance = 0
    n = math.ceil(radius / (0.6 * 1000))  # 每0.6米检测一次碰撞，次数向上取整
    for ray_point in ray_points:
        mediate_x = np.linspace(nowPoint[0], ray_point[0], n + 1)
        mediate_y = np.linspace(nowPoint[1], ray_point[1], n + 1)
        mediatePoints = np.column_stack((mediate_x, mediate_y))
        for index in range(1, len(mediatePoints)):
            mediatePoint = mediatePoints[index]
            # 检查树木
            dist, idx = tree_kdtree.query(mediatePoint, k=1)
            if dist < 600:
                totalDistance = totalDistance + (index * 0.6 * 1000)
                break
            # 检查建筑
            dist, idx = build_kdtree.query(mediatePoint, k=1)
            if dist < 600:
                totalDistance = totalDistance + (index * 0.6 * 1000)
                break
            # 检查假山
            dist, idx = mount_kdtree.query(mediatePoint, k=1)
            if dist < 600:
                totalDistance = totalDistance + (index * 0.6 * 1000)
                break
    return totalDistance / ray_n


# 计算A和B两个数的差异程度，返回0~1的数，完全相同为1
def getDifference(a, b, scale=1.0):
    diff = abs(a - b)
    # 当a和b都很大或都很小时，考虑相对差异可能更合适，这里提供一个灵活版本
    denominator = max(abs(a), abs(b), 1.0)  # 至少为1，避免除零且平衡量纲
    normalized_diff = diff / denominator
    closeness = math.exp(-scale * normalized_diff)
    return closeness


def drawTemp():
    plt.rcParams['font.family'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
    #  获取各个点的分数和点位
    all_P = []
    for i in range(1, len(paths[np.argmax(allScores)]) - 1):
        P = altPoints[paths[np.argmax(allScores)][i]]
        all_P.append(P)
    #  标准化这些点
    x_min, y_min = np.min(np.array(all_P), axis=0)
    x_max, y_max = np.max(np.array(all_P), axis=0)
    x_normalized = (np.array(all_P)[:, 0] - x_min) / (x_max - x_min) * 100
    y_normalized = (np.array(all_P)[:, 1] - y_min) / (y_max - y_min) * 100
    normal_PS = np.column_stack((x_normalized, y_normalized))
    # 初始化密度网格
    grid_size = 100
    cell_size = 10
    num_cells = grid_size // cell_size
    density_grid = np.zeros((num_cells, num_cells))
    # 计算每个点所在的网格位置
    for k in range(0, len(normal_PS)):
        normal_P = normal_PS[k]
        x, y = normal_P
        if 0 <= x < 100 and 0 <= y < 100:
            i = int(x // cell_size)
            j = int(y // cell_size)
            if i < num_cells and j < num_cells:
                density_grid[i, j] += stepScores[np.argmax(allScores)][k]
    # 绘制网格热力图
    fig, ax = plt.subplots(figsize=(10, 8))

    # 创建自定义颜色映射
    colors = ['#fff5eb', '#fee6ce', '#fdd0a2', '#fdae6b', '#fd8d3c', '#f16913', '#d94801', '#a63603', '#7f2704']
    cmap = LinearSegmentedColormap.from_list('custom_blue', colors, N=256)

    # 显示密度网格
    im = ax.imshow(density_grid.T, origin='lower', cmap=cmap,
                   extent=[0, 100, 0, 100], aspect='equal')

    # 添加颜色条
    cbar = plt.colorbar(im, ax=ax, shrink=0.8)
    cbar.set_label('异景密度', fontsize=12)

    # 设置标题和标签
    ax.set_title(f'关键点位的异景分布 地图{mapID}', fontsize=14)
    ax.set_xlabel('X 坐标', fontsize=12)
    ax.set_ylabel('Y 坐标', fontsize=12)

    # 添加网格线
    ax.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7)

    plt.tight_layout()
    plt.show()


def drawLine():
    plt.rcParams['font.family'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
    x_positions = list(range(len(everyOC_index)))  # 生成 [0, 1, 2, 3, ..., 19]

    # 2. 创建图表和坐标轴
    fig, ax = plt.subplots(figsize=(12, 6))  # 调整图形尺寸

    # 3. 绘制折线图，并自定义样式
    #    'o-' 表示数据点用圆点标记，并用实线连接
    #    你可以根据需要调整颜色、线条粗细、标记大小等
    ax.plot(x_positions, everyOC_index, 'o-', color='steelblue', linewidth=2, markersize=8, label='最优路径开阔度')

    # 4. 在每个数据点上标注数值
    for x, y in zip(x_positions, everyOC_index):
        # ha: 水平对齐方式, va: 垂直对齐方式
        # 稍微调整文本的y位置，避免重叠
        ax.text(x, y + 100, f'{y:.2f}', ha='center', va='bottom', fontsize=9, fontweight='bold')

    # 5. 添加图表装饰和信息，使图表更清晰
    ax.set_title('最优路径开阔度', fontsize=15, pad=20)
    ax.set_xlabel('数据点顺序', fontsize=12)
    ax.set_ylabel('数据值', fontsize=12)
    ax.grid(True, linestyle=':', alpha=0.7)  # 添加网格线

    # 6. 根据数据范围自动调整y轴显示范围，为标注留出空间
    y_min = min(everyOC_index)
    y_max = max(everyOC_index)
    ax.set_ylim(y_min - (y_max - y_min) * 0.05, y_max + (y_max - y_min) * 0.15)  # 调整y轴范围

    # 7. 显示图形
    plt.tight_layout()
    plt.show()


if __name__ == '__main__':
    print("--------------------------------------------------------------------------------------------")
    # 读取配置文件内容初始化
    print("【系统提示】 读取配置文件")
    init_data()
    print("【系统提示】 读取地图数据")
    print("【系统提示】 当前地图id:", mapID)
    # 读取地图数据
    allPoints = roadUtil.getClearRoadPoints(mapID, otherMap=otherMap) + buildUtil.getBuildPoints(mapID, otherMap)
    buildPoint = buildUtil.getBuildPoints(mapID, otherMap)
    treePoints = treeUtil.getTreePoints(mapID, otherMap)
    treeRadius = treeUtil.getTreeRadius(mapID, otherMap)
    waterPoints = waterUtil.getWaterPoints(mapID, otherMap)
    mountPoints = mountainUtil.getMountainPoints(mapID, otherMap)
    # KDtree
    wall_kdtree = KDTree(np.vstack(allPoints))
    tree_kdtree = KDTree(treePoints[0])
    water_kdtree = KDTree(np.vstack(waterPoints))  # 如果waterPoints是列表的列表，需要先堆叠
    build_kdtree = KDTree(np.vstack(buildPoint))
    mount_kdtree = KDTree(np.vstack(mountPoints))
    if nowQuestion == 1 or (not skipRoad):
        if nowQuestion == 1:
            print("【系统提示】 当前正在解析问题1，求解可行游园路径并进行评分")
        else:
            print("【系统提示】 当前正在解析问题2，求解可行游园路径并进行开合度分析")
        # 生成路径点
        print("【系统提示】 正在生成路径点")
        cornerPoints = roadUtil.findCornerPoints(mapID, otherMap)
        buildCenterPoints = buildUtil.getBuildCenterPoints(mapID)
        # 合并相近的路径点，增加迭代速度
        print("【系统提示】 正在优化路径点")
        if mapID <= 10:
            cornerPoints = mergePoint(cornerPoints, 4000)
            buildCenterPoints = mergePoint(buildCenterPoints, 4000)
        else:
            cornerPoints = mergePoint(cornerPoints, 500)
            buildCenterPoints = mergePoint(buildCenterPoints, 500)
        # 配置路径表
        altPoints = []
        altPoints.extend(cornerPoints)
        altPoints.extend(buildCenterPoints)
        # 针对各个地图进行补点
        if mapID == 1:
            altPoints.append((90000, 95000))
            altPoints.append((37000, 115000))
            altPoints.append((27000, 119000))
            altPoints.append((122000, 137000))
            altPoints.append((17300, 43000))
            altPoints.append((97300, 135000))
            altPoints.append((20000, 31000))
        if mapID == 3:
            altPoints.append((17826, 53995))
            altPoints.append((23088, 56446))
            altPoints.append((12000, 59500))
            altPoints.append((20646, 68316))
            altPoints.append((17264, 78800))
            altPoints.append((43500, 66100))
            altPoints.append((54000, 34400))
            altPoints.append((60200, 45400))
            altPoints.append((74800, 51000))
        if mapID == 4:
            altPoints.append((94800, 32300))
            altPoints.append((94800, 49900))
            altPoints.append((94100, 63700))
            altPoints.append((94100, 81000))
            altPoints.append((93200, 102000))
            altPoints.append((75000, 102000))
            altPoints.append((56900, 85300))
            altPoints.append((42100, 90200))
            altPoints.append((6530, 68200))
            altPoints.append((6770, 47200))
            altPoints.append((13500, 36600))
            altPoints.append((44300, 50000))
            altPoints.append((41700, 43800))
            altPoints.append((56600, 54400))
            altPoints.append((54300, 63700))
        if mapID == 5:
            altPoints.append((5900, 57400))
            altPoints.append((-307, 49000))
            altPoints.append((26900, 106000))
            altPoints.append((24900, 133000))
        if mapID == 6:
            altPoints.append((109000, 78100))
            altPoints.append((103700, 106900))
            altPoints.append((95900, 129200))
            altPoints.append((103000, 152800))
            altPoints.append((79900, 181300))
            altPoints.append((68300, 178100))
            altPoints.append((56800, 179000))
            altPoints.append((41700, 181100))
            altPoints.append((31000, 183200))
            altPoints.append((12200, 180900))
            altPoints.append((7550, 174800))
            altPoints.append((8200, 159000))
            altPoints.append((4200, 148300))
            altPoints.append((11800, 138500))
            altPoints.append((18200, 130100))
        if mapID == 7:
            altPoints.append((91500, 22700))
            altPoints.append((90042, 7226))
            altPoints.append((90209, -7086))
            altPoints.append((97199, -10082))
            altPoints.append((101193, -7086))
            altPoints.append((104189, 569))
            altPoints.append((89210, 37183))
            altPoints.append((80722, 45006))
            altPoints.append((83718, 76461))
            altPoints.append((76561, 86780))
            altPoints.append((88711, 102258))
            altPoints.append((87712, 111411))
            altPoints.append((88045, 118734))
            altPoints.append((78725, 119899))
            altPoints.append((79058, 109081))
            altPoints.append((58088, 75129))
            altPoints.append((49100, 69471))
            altPoints.append((36452, 70636))
            altPoints.append((30294, 78125))
            altPoints.append((29295, 93603))
            altPoints.append((33622, 101758))
            altPoints.append((35620, 71468))
            altPoints.append((47602, 69637))
            altPoints.append((30127, 63646))
            altPoints.append((9490, 39347))
            altPoints.append((8159, 30193))
            altPoints.append((8159, 21206))
            altPoints.append((15481, 36018))
            altPoints.append((23137, 39014))
            altPoints.append((23637, 28862))
            altPoints.append((27797, 20041))
            altPoints.append((32957, 28862))
            altPoints.append((38116, 32357))
            altPoints.append((22804, 11886))
            altPoints.append((20308, 6061))
            altPoints.append((29628, 2067))
            altPoints.append((39614, 7892))
            altPoints.append((35287, 12718))
            altPoints.append((33955, -5588))
            altPoints.append((45938, -11580))
            altPoints.append((58920, -9915))
            altPoints.append((64079, -762))
            altPoints.append((77393, 4563))
            altPoints.append((68739, 21206))
            altPoints.append((87712, 112576))
            altPoints.append((87046, 118734))
            altPoints.append((78558, 119733))
            altPoints.append((78558, 111578))
            altPoints.append((78725, 104255))
        if mapID == 8:
            altPoints.append((80577, 55098))
            altPoints.append((65068, 48338))
            altPoints.append((59977, 46747))
            altPoints.append((53296, 47940))
            altPoints.append((47410, 49531))
            altPoints.append((40332, 50167))
            altPoints.append((59977, 22170))
            altPoints.append((53932, 22409))
            altPoints.append((54251, 16284))
            altPoints.append((45183, 17716))
            altPoints.append((42400, 17159))
            altPoints.append((43036, 31635))
            altPoints.append((40411, 8490))
            altPoints.append((31980, 11910))
            altPoints.append((21720, 4831))
            altPoints.append((26970, 28613))
            altPoints.append((-3333, 58280))
            altPoints.append((-13752, 56530))
            altPoints.append((17743, 81312))
        if mapID == 9:
            altPoints.append((29144.456018, 60797.213917))
            altPoints.append((29144.456018, 54383.933152))
            altPoints.append((29144.456018, 48807.167270))
            altPoints.append((21824.950798, 48598.038550))
            altPoints.append((21755.241224, 54383.933152))
            altPoints.append((14366.026431, 54593.061873))
            altPoints.append((9695.485005, 54523.352299))
            altPoints.append((5234.072299, 54871.900167))
            altPoints.append((1400.045755, 54593.061873))
            altPoints.append((3979.299976, 47970.652388))
            altPoints.append((-4943.525435, 46855.299212))
            altPoints.append((-10032.324303, 36189.734463))
            altPoints.append((-10450.581744, 27476.037772))
            altPoints.append((-5082.944582, 27476.037772))
            altPoints.append((354.402152, 26081.846302))
            altPoints.append((702.950020, 18971.469802))
            altPoints.append((702.950020, 13255.284773))
            altPoints.append((1121.207461, 9072.710362))
            altPoints.append((3909.590402, 4820.426377))
            altPoints.append((7883.036093, 2380.591303))
            altPoints.append((12553.577519, 1125.818980))
            altPoints.append((15481.379607, 1962.333862))
            altPoints.append((20082.211460, -2289.950123))
            altPoints.append((27541.135827, -3126.465005))
            altPoints.append((27053.168812, 2310.881730))
            altPoints.append((12065.610504, 7190.551877))
            altPoints.append((25240.719900, 24478.526111))
            altPoints.append((28098.812415, 27406.328199))
            altPoints.append((28935.327297, 37653.635507))
            altPoints.append((10741.128608, 29288.486684))
            altPoints.append((1260.626608, 29079.357963))
        if mapID == 10:
            altPoints.append((93836.322231, 36377.459316))
            altPoints.append((79955.344729, 35332.654557))
            altPoints.append((50850.069322, 29959.372944))
            altPoints.append((41148.310853, 47870.311656))
            altPoints.append((47267.881579, 55930.234076))
            altPoints.append((70253.586260, 55780.976254))
            altPoints.append((81149.407310, 87572.892468))
            altPoints.append((94284.095698, 85035.509484))
            altPoints.append((108463.588846, 85781.798597))
            altPoints.append((107568.041910, 97274.650937))
            altPoints.append((100403.666425, 107573.440696))
            altPoints.append((95776.673924, 116528.910052))
            altPoints.append((89657.103198, 133096.528361))
            altPoints.append((99060.346022, 143693.833766))
            altPoints.append((110254.682717, 143395.318121))
            altPoints.append((119060.894250, 142051.997717))
            altPoints.append((128165.621429, 138022.036507))
            altPoints.append((129061.168364, 127723.246747))
            altPoints.append((137867.379898, 95035.783598))
            altPoints.append((137718.122075, 95483.557066))
            altPoints.append((130553.746590, 87572.892468))
            altPoints.append((126822.301025, 76378.555773))
            altPoints.append((99358.861667, 65631.992545))
            altPoints.append((96672.220860, 54437.655850))
            altPoints.append((95478.158279, 45034.413027))
            altPoints.append((108015.815378, 85632.540774))
            altPoints.append((107120.268442, 98916.486985))
            altPoints.append((100851.439893, 106528.635938))
            altPoints.append((97866.283441, 114439.300536))
            altPoints.append((91149.681424, 118021.488278))
            altPoints.append((85627.141988, 145484.927637))
            altPoints.append((68014.718921, 122797.738602))
        # if mapID == 11:
        #     altPoints.append((14279.899259, -1015.019440))
        #     altPoints.append((14279.899259, -521.982138))
        #     altPoints.append((14215.025930, -28.944837))
        #     altPoints.append((14150.152600, 866.307105))
        #     altPoints.append((13345.723319, 645.737786))
        #     altPoints.append((11931.484744, 567.889791))
        #     altPoints.append((11256.802121, 1839.407042))
        #     altPoints.append((8078.008993, -509.007473))
        #     altPoints.append((10685.916824, 6847.628052))
        #     altPoints.append((9025.159598, 6847.628052))
        #     altPoints.append((5132.759850, 6406.489414))
        #     altPoints.append((6378.327770, 6471.362743))
        # 将起点和终点加入路径表中
        altPoints.append((entrance_list[mapID - 1][0], entrance_list[mapID - 1][1]))
        altPoints.append((exit_list[mapID - 1][0], exit_list[mapID - 1][1]))
        # 递归获取路径规划表
        print("【系统提示】 开始递归获取路径点")
        findFeasiblePath()
        print(f"\n【系统提示】 共计找到 {len(paths)} 条路径,开始评分")
        # 对各个路段进行评分
        allScores = []
        for i in range(0, len(paths)):
            path = paths[i]
            score = scorePath(path)
            percent = (i / len(paths) * 100) + 1
            if percent > 100: percent = 100
            print(
                f'\r【系统提示】 正在进行路线评分: [{"█" * math.floor(60 * percent / 100)}{" " * math.floor(60 * (100 - percent) / 100)}] {percent:.0f}%',
                end='', flush=True)
            length = 0
            for p in range(0, len(path) - 1):
                length = length + distance(altPoints[path[p]], altPoints[path[p + 1]])
            # 每条路线的总分 = (点数量+总分) - (长度(m)*修正系数)
            a = 1  # α
            b = 1  # β
            n = 0.4  # η
            c = 120  # c
            s = 0
            s_total = score[0] + score[1] + score[2] + score[3]
            s = s + math.pow(s_total / (a * length + s_total), b)
            s = s + math.pow(len(path) / (len(path) + c), n)
            s = s * 100
            # s = ((len(path) * 5) + (score[0] + score[1] + score[2] + score[3])) - ((length / 1000) * 0.005) 废案
            allScores.append(s)
        print(f"\n【系统提示】 路线评分完成,最高分为: {np.array(allScores).max():.2f} ", )
        if nowQuestion == 1:
            # 可视化地图数据
            if draw == 0:
                print("【系统提示】 正在绘制游园路线图")
                drawMap(allPoints, cornerPoints, buildCenterPoints)
                print("【系统提示】 绘制游园路线图完成")
            if draw == 1:
                print("【系统提示】 正在绘制异景热力图")
                drawTemp()
                print("【系统提示】 绘制异景热力图完成")

        else:
            # 对最优路径进行开合度分析
            goodPath = paths[np.argmax(allScores)]
            lastOC_index = 0
            totalOC_index = 0
            everyOC_index = []
            for point_index in range(0, len(goodPath)):
                point = goodPath[point_index]
                # 显示进度条
                per = (point_index / len(goodPath) * 100) + 1
                if per > 100: per = 100
                print(
                    f'\r【系统提示】 正在进行开合度分析: [{"█" * math.floor(60 * per / 100)}{" " * math.floor(60 * (100 - per) / 100)}] {per:.0f}%',
                    end='', flush=True)

                newOC_index = getPointOC_index(altPoints[point])
                # totalOC_index = totalOC_index + math.fabs(newOC_index - lastOC_index) 废案
                totalOC_index = totalOC_index + newOC_index
                everyOC_index.append(newOC_index)
                lastOC_index = newOC_index
            totalOC_index = totalOC_index / (math.log(len(goodPath) * 10 + (mapArea * 2 / (1000 * 1000 * 1000)), 1.2))

            print(f"\n【系统提示】 开合度分析完成", )
            print(f"        -> 最小视野为: {np.array(everyOC_index).min():.2f} ", )
            print(f"        -> 最大视野为: {np.array(everyOC_index).max():.2f} ", )
            print(f"        -> 平均视野为: {np.array(everyOC_index).mean():.2f} ", )
            print(f"        -> 综合得分为: {totalOC_index:.2f} ", )
    if nowQuestion == 2:
        if skipRoad:
            print("【系统提示】 当前正在解析问题2，计算园区各种指标")
        print("【系统提示】 开始计算景观指标", mapID)
        # 计算地图水体
        print("<---水体--->")
        waterArea, waterCount = waterUtil.getWaterAreaAndCount(mapID, water_kdtree, tree_kdtree, mount_kdtree)
        waterPerimeter = waterUtil.getWaterPerimeter(mapID)
        waterS_I = waterPerimeter / (2 * math.sqrt(math.pi * waterArea))
        print(f"    水体数量为{waterCount}")
        print(f"    水体面积为{waterArea / 1000000:.2f}m²")
        print(f"    水体周长为{waterPerimeter / 1000:.2f}m")
        print(f"    水体覆盖率为{(waterArea / mapArea) * 100:.2f}%")
        print(f"    水体形状指数为{waterS_I:.2f}")
        # 计算地图假山
        print("<---假山--->")
        mountainArea, mountainCount = mountainUtil.getMountainAreaAndCount(mapID)
        mountainPerimeter = mountainUtil.getMountainPerimeter(mapID)
        mountainS_I = mountainPerimeter / (2 * math.sqrt(math.pi * mountainArea))
        print(f"    假山数量为{mountainCount}")
        print(f"    假山面积为{mountainArea / 1000000:.2f}m²")
        print(f"    假山周长为{mountainPerimeter / 1000:.2f}m")
        print(f"    假山覆盖率为{(mountainArea / mapArea) * 100:.2f}%")
        print(f"    假山形状指数为{mountainS_I:.2f}")
        # 计算地图树木
        print("<---树木--->")
        treeArea, treeCount = treeUtil.getTreeAreaAndCount(mapID)
        print(f"    树木数量为{treeCount}", )
        print(f"    树木面积为{treeArea / 1000000:.2f}m²")
        print(f"    树木覆盖率为{(treeArea / mapArea) * 100:.2f}%")
        TreeD_ob = treeUtil.getTreeD_ob(mapID, tree_kdtree)
        TreeD_ex = 1 / (2 * (math.sqrt(treeCount / mapArea)))
        print(f"    观测平均最近临距离为{TreeD_ob:.4f}")
        print(f"    期望平均最近临距离为{TreeD_ex:.4f}")
        print(f"    树木离散度为{TreeD_ob / TreeD_ex:.2f}")
        # 计算综合指标
        print("<---整体--->")
        allCount = waterCount + treeCount + mountainCount
        # 香农指数
        shannon_H = getShannon_H([waterCount / allCount, treeCount / allCount, mountainCount / allCount])
        print(f"    香农多样性指数为{shannon_H:.4f}")

        # 如果寻路过，则可以进行总分评价
        if not skipRoad:
            score = 0
            if mapID == 3:
                score = 100
                print(f"    园林综合评分为：{score:.2f}（以寄畅园为100分满）")
            else:
                # 50分给开合度
                score = score + 50 * getDifference(scoreIndex_OC, totalOC_index)
                # 15分给香农多样性指数
                score = score + 15 * getDifference(scoreIndex_H, shannon_H)
                # 15分给(山/水)比例
                MWrate = mountainArea / waterArea
                score = score + 15 * getDifference(scoreIndex_MWrate, MWrate)
                # 10分给离散度
                score = score + 10 * getDifference(scoreIndex_TreeVar, TreeD_ob)
                # 5分给山体形状
                score = score + 10 * getDifference(scoreIndex_MountainS_I, mountainS_I)
                # 5分给水体形状
                score = score + 10 * getDifference(scoreIndex_WaterS_I, waterS_I)
                print(f"    园林综合评分为：{score:.2f}（以寄畅园为100分满）")
        print("")

        print("【系统提示】 正在绘制异景折线图")
        drawLine()
        print("【系统提示】 绘制异景折线图完成")
    print("程序运行结束")
    print("--------------------------------------------------------------------------------------------")
